#include <iostream>
#include <unistd.h>
#include <thread>
#include <mutex>
#include <atomic>
#include <condition_variable>
#include <functional>

#include <vector>
#include <queue>
// 为每个线程创建独立的输出锁，避免竞争
static std::mutex output_mutex;

template <class T>
class ThreadPool
{
public:
    // 构造函数
    ThreadPool(size_t thread_num) : _thread_nums(thread_num), _is_terminate(false)
    {
        // 创建指定数量的线程放入线程容器中
        for (int i = 0; i < _thread_nums; i++)
        {
            _threads.emplace_back(new std::thread(handler_task, this));
        }
    }
    ~ThreadPool()
    {
        for (int i = 0; i < _thread_nums; i++)
        {
            if (_threads[i])
            {
                delete _threads[i];
                _threads[i] = nullptr;
            }
        }
    }

public:
    void stop()
    {
        std::cout << "调用stop" << std::endl;
        // 加锁并且设置标志位为true
        {
            std::unique_lock<std::mutex> ul(_lock);
            _is_terminate = true;
        }

        // 注意需要通知所有线程，防止部分线程在wait等待
        _cod.notify_all();

        // 1.遍历所有线程，并且调用join
        for (auto &td : _threads)
        {
            if (td->joinable())
                td->join();
        }
    }

    // 这里直接在线程函数中pop
    //  // 任务的插入与删除
    //  T pop()
    //  {
    //      T t = _tasks.front();
    //      _tasks.pop();
    //      return t;
    //  }

    void push(const T &in)
    {
        // 首先加锁
        std::unique_lock<std::mutex> ul(_lock);
        if (!_is_terminate)
        {
            _tasks.push(in);

            // 插入任务后通知线程执行任务
            _cod.notify_one();
        }
    }

private:
    // 线程执行的函数（永固具体处理用户给的任务）
    static void *handler_task(ThreadPool *tp)
    {
        while (true)
        {

            // 从任务队列中获取任务并且执行,注意需要加锁
            T t;
            {
                std::unique_lock<std::mutex> ul(tp->_lock);
                // 循环读取任务队列，如果为空，就一直在条件变量下等待

                tp->_cod.wait(ul, [tp]()
                              { return tp->_is_terminate || !tp->_tasks.empty(); });

                // 注意检查错误条件，调用stop并且任务执行完毕可以退出
                // std::cout << "正在处理任务线程的数量" << tp->_handle_thread << std::endl;
                // 每有一个线程在执行任务，让原子变量++，运行结束让原子变量--。这样就能够得到正在处理任务的线程数量。不一定要使用这个
                if (tp->_is_terminate && tp->_handle_thread == 0 && tp->_tasks.empty())
                    break;

                // 运行到这里，说明有任务了，读取任务并执行
                t = tp->_tasks.front();
                tp->_tasks.pop();
                ++(tp->_handle_thread);
            }

            auto tid = std::this_thread::get_id();
            std::cout << "线程" << tid << "正在执行任务:" << std::endl;

            auto ans = t(); // 执行任务

            std::cout << "执行的结果为:" << ans << std::endl;
            std::cout << "------------------------" << std::endl;

            {
                std::unique_lock<std::mutex> ul(tp->_lock);
                --(tp->_handle_thread);
            }
        }
        return 0;
    }

private:
    // 任务队列
    std::queue<T> _tasks{};

    // 工作的线程
    std::vector<std::thread *> _threads{};

    // 线程的数量
    size_t _thread_nums;

    // 互斥锁与条件变量
    std::mutex _lock;

    std::condition_variable _cod;

    // 线程池运行的状态
    bool _is_terminate;

    // 一个原子变量记录正在处理线程的数量
    std::atomic<int> _handle_thread{0};
};